package com.ruoyi.system.controller;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.ruoyi.domain.request.EsHouseRequest;
import com.ruoyi.domain.result.Result;
import com.ruoyi.domain.utils.StringUtils;
import com.ruoyi.domain.vo.EsHouse;
import com.ruoyi.system.service.SearchService;
import lombok.extern.log4j.Log4j2;
import org.apache.http.HttpHost;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.MatchQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.xcontent.XContentType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Map;

/**
 * @author 姚炜琦
 * @Classname SearchController
 * @Description ES房源查询（房源名称 + 地址 + 省 + 市 + 区） + 高亮实现
 * @Date 2022/10/25 19:35
 */
@RestController
@RequestMapping("/search")
@Log4j2
public class SearchController {
    @Autowired
    private HttpServletRequest request;

    @Autowired
    private SearchService searchService;

    @Autowired
    private RedisTemplate redisTemplate;

    /**
     * es同步计数器
     */
    private static Integer counterX = 0;

    /**
     * redis同步计时器
     */
    private static Integer counterY = 0;

    /**
     * 定义ES索引
     */
    private final static String INDEX_NAME = "house";
    private final RestHighLevelClient client;

    public SearchController() {
        client = new RestHighLevelClient(
                RestClient.builder(
                        new HttpHost("139.196.125.30", 9200, "http")));
        log.info("功能名称：【{}】,初始化地址：【{}】,端口号：【{}】",
                "ES初始化","139.196.125.30","9200");
    }

    /**
     * ES房源查询（房源名称 + 地址 + 省 + 市 + 区） + 高亮实现
     * @param esHouseRequest
     * @return
     */
    @PostMapping("/esHouseList")
    @ResponseBody
    public Result<ArrayList<EsHouse>> esHouseList(@RequestBody EsHouseRequest esHouseRequest){
        log.info("功能名称：【{}】，请求地址：【{}】，请求方式：【{}】，请求参数：【{}】",
                "ES房源查询（房源名称 + 地址 + 省 + 市 + 区） + 高亮实现",request.getRequestURI(),request.getMethod(), JSONObject.toJSON(esHouseRequest));

        // 用户搜索的缓存列表清除
        if (counterY != 0){
            redisTemplate.delete("esSearchList");
        }

        // 生成一个空List
        ArrayList<EsHouse> list = new ArrayList<>();
        SearchRequest searchRequest = new SearchRequest(INDEX_NAME);
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        // 高亮查询楼盘名称
        if (StringUtils.isNotEmpty(esHouseRequest.getHousesName())){
            MatchQueryBuilder housesName = QueryBuilders.matchQuery("housesName", esHouseRequest.getHousesName());
            boolQueryBuilder.must(housesName);
        }
        // 高亮查询楼盘地址
        if (StringUtils.isNotEmpty(esHouseRequest.getHousesAddress())){
            MatchQueryBuilder housesAddress = QueryBuilders.matchQuery("housesAddress", esHouseRequest.getHousesAddress());
            boolQueryBuilder.must(housesAddress);
        }
        // 高亮查询省
        if (StringUtils.isNotEmpty(esHouseRequest.getTown())){
            MatchQueryBuilder town = QueryBuilders.matchQuery("town", esHouseRequest.getTown());
            boolQueryBuilder.must(town);
        }
        // 高亮查询市
        if (StringUtils.isNotEmpty(esHouseRequest.getCity())){
            MatchQueryBuilder city = QueryBuilders.matchQuery("city", esHouseRequest.getCity());
            boolQueryBuilder.must(city);
        }
        // 高亮查询区
        if (StringUtils.isNotEmpty(esHouseRequest.getDistrict())){
            MatchQueryBuilder district = QueryBuilders.matchQuery("district", esHouseRequest.getDistrict());
            boolQueryBuilder.must(district);
        }

        searchRequest.source(searchSourceBuilder);

        // 创建高亮对象
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        // 楼盘名称拼接高亮代码
        HighlightBuilder housesName = highlightBuilder.field("housesName");
        housesName.preTags("<span style='color:red'>");
        housesName.postTags("</span>");

        // 楼盘地址拼接高亮代码
        HighlightBuilder housesAddress = highlightBuilder.field("housesAddress");
        housesAddress.preTags("<span style='color:red'>");
        housesAddress.postTags("</span>");

        // 省拼接高亮代码
        HighlightBuilder town = highlightBuilder.field("town");
        town.preTags("<span style='color:red'>");
        town.postTags("</span>");

        // 市拼接高亮代码
        HighlightBuilder city = highlightBuilder.field("city");
        city.preTags("<span style='color:red'>");
        city.postTags("</span>");

        // 区拼接高亮代码
        HighlightBuilder district = highlightBuilder.field("district");
        district.preTags("<span style='color:red'>");
        district.postTags("</span>");

        searchSourceBuilder.highlighter(highlightBuilder);
        searchSourceBuilder.query(boolQueryBuilder);
        try {
            SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
            SearchHits hits = searchResponse.getHits();
            SearchHit[] hits1 = hits.getHits();
            for (SearchHit documentFields : hits1) {
                // 获取结果集
                String sourceAsString = documentFields.getSourceAsString();
                EsHouse esHouse = JSON.parseObject(sourceAsString, EsHouse.class);
                Map<String, HighlightField> highlightFields = documentFields.getHighlightFields();
                if (highlightFields.size()>0 && highlightFields!=null){
                    // 取出高亮字段
                    HighlightField name1 = highlightFields.get("housesName");
                    HighlightField name2 = highlightFields.get("housesAddress");
                    HighlightField name3 = highlightFields.get("town");
                    HighlightField name4 = highlightFields.get("city");
                    HighlightField name5 = highlightFields.get("district");
                    // 拼接
                    if (name1!=null){
                        Text[] fragments = name1.getFragments();
                        String str="";
                        for (Text fragment : fragments) {
                            str+=fragment;
                        }
                        // 存入es对象
                        esHouse.setHousesName(str);
                    }
                    if (name2!=null){
                        Text[] fragments = name2.getFragments();
                        String str="";
                        for (Text fragment : fragments) {
                            str+=fragment;
                        }
                        esHouse.setHousesAddress(str);
                    }
                    if (name3!=null){
                        Text[] fragments = name3.getFragments();
                        String str="";
                        for (Text fragment : fragments) {
                            str+=fragment;
                        }
                        esHouse.setTown(str);
                    }
                    if (name4!=null){
                        Text[] fragments = name4.getFragments();
                        String str="";
                        for (Text fragment : fragments) {
                            str+=fragment;
                        }
                        esHouse.setCity(str);
                    }
                    if (name5!=null){
                        Text[] fragments = name5.getFragments();
                        String str="";
                        for (Text fragment : fragments) {
                            str+=fragment;
                        }
                        esHouse.setDistrict(str);
                    }
                }
                // 存入ES列表
                list.add(esHouse);
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

        // 判断用户搜索的省份是否有数据
        if (list.size() == 0) {
            return Result.error(null,"暂时未收录该省份信息，给您带来不便!");
        }

        // 往redis里创建一个当前搜索出的记录列表
        ValueOperations ops = redisTemplate.opsForValue();
        ops.set("esSearchList",list);

        // 计数器++
        counterY++;

        // 打印日志
        log.info("功能名称：【{}】，请求地址：【{}】，请求方式：【{}】，响应结果：【{}】",
                "ES房源查询（房源名称 + 地址 + 省 + 市 + 区） + 高亮实现",request.getRequestURI(),request.getMethod(), JSONObject.toJSON(Result.success(list)));
        return Result.success(list);
    }

    /**
     * 当search服务启动时 将mysql里的房源数据同步到  ES索引库里面
     */
    @Scheduled(cron = "0 0/1 * * * ?")
    public void EsHouseSync(){

        // 通过计数器判断当ES同步状态
        if (counterX != 0){
            // 创建搜索索引
            SearchRequest searchRequest = new SearchRequest(INDEX_NAME);

            try {
                SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
                // 循环结果集
                for (SearchHit hit : searchResponse.getHits().getHits()) {
                    String sourceAsString = hit.getSourceAsString();
                    // json合适转换
                    EsHouse esHouse = JSONObject.parseObject(sourceAsString, EsHouse.class);
                    // 执行es索引删除
                    DeleteRequest deleteRequest = new DeleteRequest(INDEX_NAME, esHouse.getId());
                    client.delete(deleteRequest,RequestOptions.DEFAULT);
                }
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }

        // 查询所有的房源数据
        ArrayList<EsHouse> houseList = searchService.selectAllHouse();
        // 将房源列表遍历循环
        for (EsHouse house : houseList) {
            try {
                // 创建索引
                IndexRequest indexRequest = new IndexRequest(INDEX_NAME);
                // 将每个房源对象转换为es可接受的JSON格式
                String json = JSONObject.toJSONString(house);
                // ID赋予
                indexRequest.id(house.getId());
                // 其他信息赋予
                indexRequest.source(json, XContentType.JSON);
                // 执行添加索引
                client.index(indexRequest,RequestOptions.DEFAULT);
            }catch (IOException e) {
                // 捕捉异常
                throw new RuntimeException(e);
            }
        }
        // es同步计数器 + 1
        counterX++;

        log.info("同步成功!");
    }
}
